package fsmsim.parserXML;



import fsmsim.buffer.Buffer;
import fsmsim.buffer.BufferFIFO;
import fsmsim.buffer.BufferLIFO;
import fsmsim.dataStructure.Container;
import fsmsim.dataStructure.ContainerVector;
import fsmsim.dataStructure.Event;
import fsmsim.dataStructure.FSM;
import fsmsim.dataStructure.State;
import fsmsim.dataStructure.Transition;
import fsmsim.simulator.Controller;

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;

import org.xml.sax.helpers.DefaultHandler;

import java.io.*;
import java.util.StringTokenizer;
import javax.xml.validation.*;
import org.xml.sax.SAXException;

/**
 * <p>Title: FSMSim</p>
 *
 * <p>Description: Simulatore di macchine a stati finiti.</p>
 *
 * <p>Copyright: Copyright (c) 2008</p>
 *
 * <p>Company: Bollati, Donati, Gabrielli, Peli</p>
 *
 * @author Bollati, Donati, Gabrielli, Peli
 * @version 2.0
 */
public class ParserXML extends DefaultHandler {

    private String fileXML;
    
    private Container eventsList;
    private Container transitionsList;
    private Container externalEventsList;
    private Vector FSMList = new Vector();
    private Buffer buffer;
    private String tempVal;    //to maintain context
    private Event event;
    private Transition transition;
    private State initialState;
    private String fsmType;
    private FSM fsm;
    private int policyType;
    private String bufferType;
    private int dimension;

    public ParserXML(String fileXML) {
        this.fileXML = fileXML;
    }
    
    public void doParsingXML(Controller controller) {
        parseDocument();
        controller.setOutputEventList(externalEventsList);
        controller.setFsms(FSMList);
        controller.setBuffer(buffer);
    }

    private void parseDocument() {

        //getEventByPosition a factory
        SAXParserFactory spf = SAXParserFactory.newInstance();
        try {

            //getEventByPosition a new instance of parser
            SAXParser sp = spf.newSAXParser();

            //parse the file and also register this class for call backs
            //sp.parse(this.fileXML, this);
            //System.out.println(fileXML);
            sp.parse(new java.io.File(fileXML),this);
            //sp.parse(fileXML,this);

        } catch (SAXException se) {
            se.printStackTrace();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }


    //Event Handlers
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        //reset
        if (qName.equalsIgnoreCase("ListaEvento")) {
            // crea la lista degli eventi esterni
            eventsList = new ContainerVector();
        }

        if (qName.equalsIgnoreCase("MacchinaStatiFiniti")) {
            // crea la FSM
            fsm = new FSM();
            transitionsList = new ContainerVector();
        }

        if (qName.equalsIgnoreCase("DescrizioneBuffer")) {
            // crea il buffer
            eventsList = new ContainerVector();
        }
    }

    /*
     * Metodo che effettua il parsing
     * */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        tempVal = new String(ch, start, length).trim();
    }

    @Override
    public void endElement(String uri, String localName, String qName) {

        if (qName.equalsIgnoreCase("ListaEvento")) {
            externalEventsList = eventsList;
            eventsList = new ContainerVector();
        }

        if (qName.equalsIgnoreCase("MacchinaStatiFiniti")) {
            /**
             * Controlli di consistenza prima di aggiungere
             * eventi
             * stati
             * statoinjiziale
             * 
             */
            fsm.setInitialState(initialState);
            // tokenizzare le transizioni
            fsm.setTransitions(transitionsList);
            
            if (fsmType.equalsIgnoreCase(FSM.READ)) 
                fsm.setType(FSM.READ);
            else 
                if (fsmType.equalsIgnoreCase(FSM.WRITE))
                    fsm.setType(FSM.WRITE);
                else
                    fsm.setType(FSM.DEFAULT);

            FSMList.add(fsm);


        }

        if (qName.equalsIgnoreCase("DescrizioneBuffer")) {
            /**
             * Controlli di consistenza prima di aggiungere
             * eventi
             * politica
             * fifo lifo
             * dimension
             */
            dimension = 2;

            if (this.bufferType.equalsIgnoreCase(Buffer.FIFO)) {
                buffer = new BufferFIFO(policyType, dimension);
            }
            if (this.bufferType.equalsIgnoreCase(Buffer.LIFO)) {
                buffer = new BufferLIFO(policyType, dimension);
            }

            for (int i = 0; i < this.eventsList.size(); i++) {
                // a seconda del tipo di buffer dovrebbe aggiunger in posi
                // zione corretta
                buffer.addEvent((Event) this.eventsList.getElement(i));
            }
        }

        if (qName.equalsIgnoreCase("evento")) {
            event = new Event(tempVal);
            if (tempVal.compareTo("")==0)
                    event = null;
            eventsList.addElement(event);
        }


        if (qName.equalsIgnoreCase("statoIniziale")) {
            initialState = new State(tempVal);

        }
        
        
        if (qName.equalsIgnoreCase("tipoFsm")) {
            fsmType = new String(tempVal);
        }

        if (qName.equalsIgnoreCase("transizione")) {
            try {

                transition = loadTransition(new String(tempVal));

                transitionsList.addElement(transition);
            } catch (Exception ex) {
                Logger.getLogger(ParserXML.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        if (qName.equalsIgnoreCase("politicaGestione")) {
            policyType = Integer.parseInt(new String(tempVal));
        }

        if (qName.equalsIgnoreCase("tipoBuffer")) {
            bufferType = new String(tempVal);
        }

        if (qName.equalsIgnoreCase("dimensione")) {
            dimension = Integer.parseInt(new String(tempVal));
        }

    }

    private Transition loadTransition(String transizione) throws Exception {
        //per separare gli elementi transiz. usa "|" nel file
        //es. <transizione>1|a|D|4</transizione>
        StringTokenizer token = new StringTokenizer(transizione, "|");

        Transition t = new Transition(transizione);
        int counter = 0;
        if (token.hasMoreTokens()) {
            String s = token.nextToken();

            State startState = new State(s);
            t.addElement(startState);
            counter++;
        }
        if (token.hasMoreTokens()) {
            String s = token.nextToken();

            Event inputEvent = new Event(s);
            if (s.equalsIgnoreCase("null")) {
            inputEvent = null;
            }
            
            t.addElement(inputEvent);
            counter++;
        }
        if (token.hasMoreTokens()) {
            String s = token.nextToken();

            Event outputEvent = new Event(s);
            if (s.equalsIgnoreCase("null")) {
            outputEvent = null;
            }

            t.addElement(outputEvent);
            counter++;
        }
        if (token.hasMoreTokens()) {
            String s = token.nextToken();

            State endState = new State(s);
            t.addElement(endState);
            counter++;
        }
        if (token.hasMoreTokens()) {
            counter++; 
        }
        //controlla se vengono scritti più di 4 elementi
        if (counter < 4 || counter > 4) {
            throw new Exception(
                    "Non hai inserito una transizione valida.");
        }
        return t;
    }
   
}
    
    
    
    
    
    
    
